[IMP] purchase: add global discount support on RFQ lines#1160
[IMP] purchase: add global discount support on RFQ lines#1160mekot-odoo wants to merge 4 commits intoodoo:19.0from
Conversation
This commit introduces a global discount mechanism that allows users to apply a percentage or fixed amount discount across all RFQ lines in a single action, improving usability and efficiency.
Prevented division by zero when converting a fixed discount to percentage if the RFQ untaxed amount is zero.
Adjusted the placement of the Discount button in the purchase order form view to align it to the right side for better visibility and usability when applying global discounts.
kcv-odoo
left a comment
There was a problem hiding this comment.
Thank you for the PR!
It seems not working properly and giving weird behavior in different cases, Can you explain bit more about what you did? why you did?
From functional POV:
-
In one case, the amount becomes 0, and in another case it shows 10 after applying the global discount.
-
Issue with Global Discount Update: If I change the global discount from 10 to 1, the amount becomes 0, which should not happen.
The current logic (especially in the wizard) does not seem correct and needs to be reviewed.
could you try to make it work Properly according to Specs and add remaining things missing from the specs
@bit-odoo Did you reviewed this task? as it seems wrong and not working properly
Have a nice day!
| @api.depends("discount", "discount_type") | ||
| def _calculate_percentage(self): | ||
| order = self.env["purchase.order"].browse(self.env.context.get("active_id")) | ||
| if self.discount_type == "percent": | ||
| self.discount_percent = self.discount | ||
| elif order.amount_untaxed != 0: | ||
| self.discount_percent = (self.discount * 100) / order.amount_untaxed | ||
| else: | ||
| self.discount_percent = 0 | ||
|
|
||
| def action_apply_discount(self): | ||
| order = self.env["purchase.order"].browse(self.env.context.get("active_id")) | ||
| if self.discount_type == "percent": | ||
| order.order_line.write({"discount": self.discount}) | ||
| elif order.amount_untaxed != 0: | ||
| order.order_line.write({"discount": 0}) | ||
| self.discount = (self.discount * 100) / order.amount_untaxed | ||
| order.order_line.write({"discount": self.discount}) | ||
| else: | ||
| raise UserError( | ||
| "Cannot apply fixed discount because Total Amount is already zero." | ||
| ) |
There was a problem hiding this comment.
This seems so wrong and giving me very weird behaviors, Could you try to understand it more and make it work properly? You can take inspiration from Discount on SO and Loyalty module or even try to use it's logic but it should not give any weird behavior 😉
There was a problem hiding this comment.
"Hello! Thank you for the feedback. After further investigation, I identified why the behavior was inconsistent: my previous logic was calculating the discount against the current amount_untaxed, which already included previous discounts. This caused the total to drift incorrectly when updating the value (e.g., changing from 10% to 1%).
To fix this and align with the standard logic used in SO/Loyalty:
Consistent Base: I am refactoring the calculation to use the Base Untaxed Amount (Sum of price_unit * product_qty) so the discount is always calculated against the original 100% value.
Standard Write: I’m removing the manual reset to zero and instead performing a single, clean write() to the lines based on this consistent base total.
Validation: I'm adding constraints to ensure the discount stays within a logical 0-100% range.
| <form> | ||
| <field name="discount"/> | ||
| <field name="discount_type" nolabel="True"/> | ||
| <span class="text-muted"> | ||
| ( | ||
| <field name="discount_percent" nolabel="1"/> | ||
| % | ||
| ) | ||
| </span> | ||
| <footer> | ||
| <button name="action_apply_discount" string="Apply" type="object" class="btn btn-primary"/> | ||
| <button special="cancel" string="Discard" class="btn btn-secondary" data-hotkey="x"/> | ||
| </footer> | ||
| </form> |
There was a problem hiding this comment.
Make it more user friendly currently is seems very ugly 🙁
purchase_discount/__manifest__.py
Outdated
| "application": True, | ||
| "installable": True, |
There was a problem hiding this comment.
It should not be application and auto_install should be true
There was a problem hiding this comment.
Thank you for the suggestion.
I’ve updated the manifest accordingly — the module is no longer marked as an application, and auto_install has been set to True.
| def action_purchase_global_discount(self): | ||
| self.ensure_one() | ||
| return { | ||
| "name": "Discount", | ||
| "type": "ir.actions.act_window", | ||
| "res_model": "purchase.order.discount", | ||
| "view_mode": "form", | ||
| "target": "new", | ||
| } |
There was a problem hiding this comment.
Make button type="action instead object
There was a problem hiding this comment.
I’ve replaced the object method call with a type="action" button and linked it directly to the corresponding window action.
| @@ -0,0 +1,2 @@ | |||
| id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | |||
| access_purchase_order_discount,access_purchase_order_discount,model_purchase_order_discount,base.group_user,1,1,1,1 | |||
There was a problem hiding this comment.
Is it really useful for non purchase user?
There was a problem hiding this comment.
Thank you for pointing it out.
You’re right — granting access to base.group_user would allow all internal users to manage purchase order discount records, which is not necessary. This feature is intended to be used only by purchase users.
I will update the access rights to restrict it to group_purchase_user instead of all internal users.
Compute the discount using the sum of (price_unit * quantity) instead of amount_untaxed to avoid inconsistent results when updating discounts. Add a constraint to restrict the discount percentage between 0 and 100 and properly initialize the base amount to prevent UnboundLocalError.

This PR introduces a global discount mechanism that allows users to
apply a percentage or fixed amount discount across all RFQ lines in a
single action, improving usability and efficiency.